"""
上下文加载工具

这个模块提供了用于加载和组织索引文件内容的功能。
主要用于从.index目录中读取所有索引文件，并将它们组织成统一的格式。

典型用法:
    # 使用默认的工作目录下的.index
    context = ContextLoader.get_indexed_context()
    
    # 或者指定索引目录
    context = ContextLoader.get_indexed_context("/path/to/index")

输出格式:
    文件内容以以下格式组织:
    file1.py.txt
    [file1的内容...]
    ###
    path/to/file2.py.txt
    [file2的内容...]
    ###
    ...

注意:
    - 所有文件路径都是相对于索引目录的相对路径
    - 文件按照相对路径字母顺序排序
    - 文件之间使用'###'分隔
    - 每个文件的内容前面是其相对路径
"""
import os
from typing import List, Tuple
from src.config import GlobalConfig
from src.utils.runtime_state import RuntimeState

class ContextLoader:
    """
    上下文加载器类
    
    这个类提供了用于加载索引文件内容的静态方法。
    它会递归遍历索引目录，读取所有文件的内容，
    并将它们组织成统一的格式。
    """
    
    @staticmethod
    def collect_files(directory: str) -> List[Tuple[str, str]]:
        """
        递归收集目录下的所有文件及其内容
        
        这个方法会递归遍历指定目录，收集所有文件的相对路径
        和内容。它会跳过无法读取的文件，并打印错误信息。
        同时会跳过.git、__pycache__等特殊目录。
        
        Args:
            directory: 要遍历的目录的完整路径
            
        Returns:
            List[Tuple[str, str]]: 包含(相对路径, 文件内容)的列表
            
        注意:
            - 相对路径是相对于index_dir的
            - 如果文件读取失败，会打印错误信息并继续处理其他文件
            - 如果目录遍历失败，会打印错误信息并返回已收集的结果
            - 会跳过.git、__pycache__等特殊目录
        """
        results = []
        
        project_type = GlobalConfig.get_config('project_type')
        skip_dirs, skip_files = ContextLoader.get_file_scan_skips(project_type)
        try:
            for entry in os.scandir(directory):
                # 跳过特定目录
                if entry.is_dir() and entry.name in skip_dirs:
                    continue

                if entry.is_dir() and entry.name.startswith('.'):
                    continue
                    
                # 跳过特定文件
                if entry.is_file() and entry.name in skip_files:
                    continue
                    
                if entry.is_file():
                    # 计算相对路径
                    rel_path = os.path.relpath(entry.path, directory)
                    # 读取文件内容
                    try:
                        with open(entry.path, 'r', encoding='utf-8') as f:
                            content = f.read()
                        results.append((rel_path, content))
                    except Exception as e:
                        print(f"读取文件失败 {rel_path}: {str(e)}")
                
                elif entry.is_dir():
                    # 递归处理子目录
                    results.extend(ContextLoader.collect_files(entry.path))
                    
        except Exception as e:
            print(f"遍历目录失败 {directory}: {str(e)}")
            
        return results

    @staticmethod
    def get_indexed_context(index_dir: str = None) -> str:
        """
        获取索引目录下所有文件的上下文
        
        这个方法会递归遍历指定目录，读取所有文件的内容，
        并将它们组织成统一的格式。每个文件的内容前面会
        添加其相对路径，文件之间使用'###'分隔。
        
        Args:
            index_dir: 索引目录的路径。
                      如果为None，则使用工作目录下的.index目录
                      
        Returns:
            str: 包含所有文件相对路径和内容的字符串。
                 格式为：
                 file1.txt
                 [content1]
                 ###
                 file2.txt
                 [content2]
                 
        注意:
            - 如果index_dir为None且未设置工作目录，返回空字符串
            - 如果指定的目录不存在，返回空字符串
            - 文件按相对路径排序
            - 所有文件都以UTF-8编码读取
        """
        if index_dir is None:
            work_dir = GlobalConfig.get_work_dir()
            if not work_dir:
                return ""
            index_dir = work_dir
        if not os.path.exists(index_dir):
            return ""
        
        # 收集所有文件
        files = ContextLoader.collect_files(index_dir)
        
        # 按相对路径排序，使输出稳定
        files.sort(key=lambda x: x[0])
        
        # 构建结果字符串
        result_parts = []
        for rel_path, content in files:
            result_parts.append(f"{rel_path}\n{content}")
        
        # 用'###'连接各部分
        return "\n###\n".join(result_parts) 

    @staticmethod
    def get_directory_tree(root_dir: str = None, prefix: str = "") -> str:
        """
        获取目录的树状结构表示，只显示Python文件
        
        这个方法会递归遍历目录，生成类似tree命令的输出。
        只显示.py文件和包含.py文件的目录。
        
        Args:
            root_dir: 要遍历的根目录。如果为None，则使用工作目录
            prefix: 用于递归时的前缀字符串，默认为空
            
        Returns:
            str: 目录结构的树状表示，类似：
                root/
                ├── src/
                │   ├── config.py
                │   └── utils/
                │       └── context_loader.py
                └── tests/
                    └── test_config.py
        """
        if root_dir is None:
            root_dir = GlobalConfig.get_work_dir()
            if not root_dir:
                return ""
        if not os.path.exists(root_dir):
            return ""
            
        def _has_files(directory: str) -> bool:
            """检查目录是否包含文件（递归）"""
            try:
                for entry in os.scandir(directory):
                    if entry.is_file():
                        return True
                    elif entry.is_dir():
                        if _has_files(entry.path):
                            return True
                return False
            except Exception:
                return False
            
        def _get_tree(path: str, prefix: str = "") -> List[str]:
            entries = []
            # 过滤并排序条目：只保留.py文件和包含.py文件的目录
            valid_items = []
            
            for entry in os.scandir(path):
                if entry.is_file():
                    valid_items.append(entry)
                elif entry.is_dir() and _has_files(entry.path):
                    if entry.name.startswith('.'):
                        continue
                    valid_items.append(entry)
                    
            # 按照目录在前，文件在后，然后按名称排序
            valid_items.sort(key=lambda x: (not x.is_dir(), x.name))
            
            for i, entry in enumerate(valid_items):
                is_last = i == len(valid_items) - 1
                connector = "└── " if is_last else "├── "
                
                entries.append(prefix + connector + entry.name + ("/" if entry.is_dir() else ""))
                
                if entry.is_dir():
                    new_prefix = prefix + ("    " if is_last else "│   ")
                    entries.extend(_get_tree(entry.path, new_prefix))
                    
            return entries
            
        try:
            base_name = os.path.basename(root_dir.rstrip(os.sep)) + "/"
            tree_lines = [base_name] + _get_tree(root_dir)
            return "\n".join(tree_lines)
        except Exception as e:
            print(f"生成目录树失败: {str(e)}")
            return ""

    @staticmethod
    def get_project_summary() -> str:
        """
        获取项目中所有文件的总结信息
        
        首先从RuntimeState获取summaries，如果不存在则从summary_cache.txt读取。
        将所有文件的总结信息格式化为XML格式。
        
        Returns:
            str: 格式化后的项目总结信息
            格式示例:
            <FILE_LIST>
            <FILE_ITEM>
                <FILE>/path/to/file.py</FILE>
                <SUMMARY>文件的总结信息...</SUMMARY>
            </FILE_ITEM>
            </FILE_LIST>
        """
        # 首先尝试从RuntimeState获取summaries
        summaries = RuntimeState.get('summaries')
        
        # 如果RuntimeState中没有，则从文件读取
        if not summaries:
            try:
                from src.utils.context_distill import load_summary_cache_file
                load_summary_cache_file()
                summaries = RuntimeState.get('summaries')
            except Exception as e:
                print(f"读取summary缓存文件失败: {str(e)}")
                return ""
        
        # 如果仍然没有summaries，返回空字符串
        if not summaries:
            return ""
        
        # 构建XML格式的输出
        result_parts = ['<FILE_LIST>']
        
        # 对文件路径进行排序，确保输出顺序稳定
        for file_path in sorted(summaries.keys()):
            summary = summaries[file_path]
            # 构建单个文件的XML块
            file_item = [
                '<FILE_ITEM>',
                f'    <FILE>{file_path}</FILE>',
                f'    <SUMMARY>{summary}</SUMMARY>',
                '</FILE_ITEM>'
            ]
            result_parts.append('\n'.join(file_item))
        
        result_parts.append('</FILE_LIST>')
        
        # 返回完整的XML字符串
        return '\n'.join(result_parts)

    @staticmethod
    def get_files_content(file_paths: List[str]) -> str:
        """
        读取多个文件的内容并按特定格式拼接
        
        遍历文件路径列表，读取每个存在的文件的内容，
        将文件路径和内容用换行符拼接，多个文件之间用'###'分隔。
        
        Args:
            file_paths: 文件路径列表
            
        Returns:
            str: 拼接后的字符串，格式为：
                file1.py
                [file1的内容]
                ###
                file2.py
                [file2的内容]
                ###
                ...
                
        示例:
            >>> paths = ['/path/to/file1.py', '/path/to/file2.py']
            >>> content = ContextLoader.get_files_content(paths)
            >>> print(content)
            /path/to/file1.py
            def hello():
                print("Hello")
            ###
            /path/to/file2.py
            def world():
                print("World")
        """
        if not file_paths:
            return ""
        
        result_parts = []
        
        for file_path in file_paths:
            try:
                # 检查文件是否存在
                if not os.path.exists(file_path):
                    print(f"文件不存在: {file_path}")
                    continue
                    
                # 读取文件内容
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    
                # 拼接文件路径和内容
                result_parts.append(f"{file_path}\n{content}")
                
            except Exception as e:
                print(f"读取文件失败 {file_path}: {str(e)}")
                continue
        
        # 用'###'连接所有部分
        return "\n###\n".join(result_parts) if result_parts else ""

    @staticmethod
    def collect_paths(directory: str, skip_dirs: List[str] = [], skip_files: List[str] = []) -> List[str]:
        """
        递归收集目录下的所有文件路径
        
        这个方法会递归遍历指定目录，收集所有文件的相对路径。
        它会跳过特定的目录和文件，并打印错误信息。
        
        Args:
            directory: 要遍历的目录的完整路径
            skip_dirs: 要跳过的目录名列表
            skip_files: 要跳过的文件名列表
            
        Returns:
            List[str]: 包含所有文件相对路径的列表
            
        注意:
            - 相对路径是相对于directory的
            - 如果目录遍历失败，会打印错误信息并返回已收集的结果
            - 会跳过以.开头的目录
        """
        results = []
        
        skip_dirs = skip_dirs or []
        skip_files = skip_files or []
        
        try:
            for entry in os.scandir(directory):
                # 跳过特定目录
                if entry.is_dir() and entry.name in skip_dirs:
                    continue
                if entry.is_dir() and entry.name.startswith('.'):
                    continue

                # 跳过特定文件
                if entry.is_file() and entry.name in skip_files:
                    continue
                    
                if entry.is_file():
                    # 计算相对路径
                    rel_path = os.path.relpath(entry.path, directory)
                    results.append(rel_path)
                
                elif entry.is_dir():
                    # 递归处理子目录
                    sub_results = ContextLoader.collect_paths(entry.path, skip_dirs, skip_files)
                    # 将子目录的路径添加到结果中，并加上子目录名
                    for sub_path in sub_results:
                        results.append(os.path.join(entry.name, sub_path))
                
        except Exception as e:
            print(f"遍历目录失败 {directory}: {str(e)}")
            
        return results

    
    @staticmethod
    def get_file_scan_skips(project_type: str) -> Tuple[List[str], List[str]]:
        """
        从配置文件中获取文件扫描的跳过目录和文件列表

        Args:
            project_type: 项目类型，如'java'或'python'

        Returns:
            Tuple[List[str], List[str]]: 包含跳过目录和文件列表的元组
        """
        dir_skip = GlobalConfig.get_config(project_type + '_dir_skip').split(',')
        file_skip = GlobalConfig.get_config(project_type + '_file_skip').split(',')
        dir_skip = [dir.strip() for dir in dir_skip]
        file_skip = [file.strip() for file in file_skip]
        return dir_skip, file_skip
    


